# Methods for reporting and creation of report classes
#
# Reporting should be handled flexible, we want to be able to specify different
# output formats, or submit individual sections to a database, that can be queried
# for report generation.
# A generic report() function will take the necessary information and will be configured
# with a Report class which determines the target format, and optional filtering of
# low priority messages. report() will then relay the remaining information to the
# appropriate output format.
# To add new output formats derive a new class from report and implement the startReport, finalizeReport, 
# and addToReport functions with their signature containing the newly defined class
###############################################################################

# For understanding the code note that the report passed to a reporting method is not necessarily the currently active report
# i.e. the one data should be written to.

#see below
setGeneric(name="startReport", def=function(report, name) standardGeneric("startReport") )
#' Opens a new Report
#' 
#' A new report is opened, the type of the report is determined by the type of the Report object passed as an
#' argument. For Html reports this will cause a file named "value of name".html and a directory named "plots" to be generated,
#' unless the Html file already exists in that case the report will be appended to the existing one.
#' The path of the newly created files can be set calling setLocation() before starting the report.
#' To be implemented : (If a report is already started, a new report file will be created, all content will be written to the new file until
#' finalize report is called. A link to the newly created file will be appended to the report that was started when the
#' call to startReport was made. (This file and link based structure is only used for the HTML report 
#' (the only one currently implemented)))    
#' @param report An object of type "Report" that defines the settings for reporting and identifies a particular report.
#' @param name A character string specifying the base name of the html file.
#' @author Bastian R. Angermann
#' @aliases startReport startReport-methods startReport,HtmlReport,character-method
#' @export
setMethod("startReport", signature(report="HtmlReport", name="character"), function (report, name="index"){
    # is a report already in progress, i.e. are we starting a sub-report?
    if ( getValue(report,"reportInProgress") )
    {
        
        # create child
        childReport <- defaultReport(FALSE)
        currentReport <- getValue(report,"currentReport")
        setValue(childReport,"parentReport", currentReport)
        setValue(report,"childReport", c(getValue(report, "childReport"), childReport))
        setValue(childReport,"masterReport",getValue(report, "masterReport"))
        
        startReport(childReport, name)
        relativePath <- basename(getValue(childReport, "name"))
        htmlRef <- paste('<a href="', relativePath, '">', name, '</a><br />', "\n", sep="")
        cat(htmlRef,file=getValue(currentReport, "name"), append = TRUE)
    } else
    {  
        # test if a report exists in the specified location and continue it if possible
        htmlFile <- file.path(report@location(), name )
        if ( file.exists( paste(htmlFile,"html",sep=".") ))
        {
            # TODO make sure the <body> tag is not yet closed.
            setValue(report,"name",basename(paste(htmlFile,"html",sep=".")))
               
        } else
        {
            if ( !file.exists( file.path(report@location(),"plots") )) # create plots sub directory if necessary 
                dir.create(path=file.path(report@location(),"plots"))# TODO trycatch 
            file <- HTMLInitFile(outdir = report@location(), filename=basename(htmlFile))
            setValue(report,"name",file)
            HTMLCSS(file=getValue(report,"name"))
        }
        setValue(getValue(report,"masterReport"),"currentReport",report)
        setValue(report,"reportInProgress",TRUE)
        setValue(report,"item",list())
    }
})

#' Used for deferring write operations, needed for parallel processing. For internal use only.
#' 
#' @param report The report class to which the report item is saved
#' @param item the character string that can be parsed into a call that represents an item in a report. 
saveItem <- function(report,item)
{
    newItem <- getValue(report,"item")
    itemNo <- length(newItem)

    newItem[[itemNo+1]] <- item

    setValue(report,"item", newItem  )  
}

#' Execute deferred operations
executeItems <- function(report)
{
    reportItems <- getValue(report,"item")
    rm("item",envir=report@referenceContainer)
    lapply(reportItems, eval)
}



#see below
setGeneric(name="finalizeReport", def=function(report) standardGeneric("finalizeReport") )
#' Closes an open report.
#'
#' Any actions necessary to finalize a report are taken.
#' For Html files this means that any open tags and the file itself will be closed
#' @param report An object of type "Report" that identifies a particular report.
#' @author Bastian R. Angermann
#' @aliases finalizeReport finalizeReport-methods finalizeReport,HtmlReport-method
#' @export
setMethod("finalizeReport" ,signature(report="HtmlReport"), function(report){
    currentReport <- getValue(getValue(report,"masterReport"),"currentReport")
    if ( getValue(currentReport ,"plotInProgress") )
    {    
        print("A plot is still in Progress. Finalizing the plot before finalizing the report.")
        finalizePlot(currentReport)
    }
    
    # execute deferred writes.
    reportItems <- getValue(report,"item")
    rm("item",envir=report@referenceContainer)
    lapply(reportItems, eval)
    
    theFile <- getValue(currentReport ,"name")
    setValue(getValue(report,"masterReport"),"currentReport",getValue(currentReport,"parentReport") )
    HTMLEndFile(file = theFile)
})

#see below
setGeneric(name="addToReport", def=function(report, text, object) standardGeneric("addToReport") )
#see below
setMethod("addToReport",signature(report="HtmlReport", text="character", object="ANY"), function(report, text, object)
{
    currentReport <- getValue(getValue(report,"masterReport"),"currentReport")
    theFile <- getValue(currentReport,"name")
    eText <- escapeHtml(text)
    saveItem(report,substitute(HTML(x=eText,file = theFile) ))
    dpObject <- object
    saveItem(report,substitute(HTML(x=dpObject,file = theFile)))
})

#' Adds content to a report.
#' 
#' An arbitrary R object together with explanatory text is added to a report.
#' The details how objects will be represented in a report depend on both the type of the object
#' and the type of the report. 
#' @param report An object of type "Report" that identifies a particular report (only HtmlReport is supported by now).
#' @param text A character string explaining the object added to the report.
#' @param object An optional object for insertion into the report.
#' @author Bastian R. Angermann
#' @aliases addToReport addToReport-methods addToReport,HtmlReport,character,ANY-method addToReport,HtmlReport,character,missing-method
#' @export
setMethod("addToReport",signature(report="HtmlReport", text="character", object="missing"), function(report, text, object)
{
    currentReport <- getValue(getValue(report,"masterReport"),"currentReport")
    theFile <- getValue(currentReport,"name")
    eText <- escapeHtml(text)
    saveItem(report,substitute(HTML(x=eText,file = theFile) ))
})


#see below
setGeneric(name="addPlotToReport",
        def=function(report, caption, plotCall,dimensions) standardGeneric("addPlotToReport") )

#see below
setMethod("addPlotToReport",signature(report="HtmlReport", caption="character", plotCall="ANY",dimensions="missing"), function(report, caption, plotCall,dimensions)
{
    call <- quote(plotCall)# prevent the plot command from being evaluated to early
    plotResolution <- getValue(report,"resolution")
    plotWidth <- getValue(report,"width")
    plotHeight <- getValue(report,"height")
    addPlotToReport(report,caption,call,dimensions=c(plotResolution,plotWidth,plotHeight))        
})
#' Adds a plot generated by a single plotting command to a report.
#' 
#' A plot is created and added to a report.
#' @param report An object of type "Report" that identifies a particular report (only HtmlReport is supported by now).
#' @param caption A character vector containing the caption of the plot
#' @param plottCall A function call creating the plot
#' @param dimensions a numeric vector with 3 elements: width [inch], height [inch], and resolution[dpi] this argument is optional if missing it will be replaced by the default settings
#' @author Bastian R. Angermann
#' @aliases addPlotToReport addPlotToReport-methods addPlotToReport,HtmlReport,character,ANY,numeric-method addPlotToReport,HtmlReport,character,ANY,missing-method
#' @export
setMethod("addPlotToReport",signature(report="HtmlReport", caption="character", plotCall="ANY",dimensions="numeric"), function(report, caption, plotCall,dimensions)
{
    currentReport <- getValue(getValue(report,"masterReport"),"currentReport")
    call <- quote(plotCall)# prevent the plot command from being evaluated to early
    # try reading the resolution settings
    if ( length(dimensions)>=3 )
        plotResolution <- dimensions[3]
    else
        plotResolution <- getValue(currentReport,"resolution")

    plotPixelWidth <- plotResolution*dimensions[1] # guaranteed by method
    # try reading height
    if ( length(dimensions)>=2 )
        plotPixelHeight <- plotResolution*dimensions[2]
    else
        plotPixelHeight <- getValue(currentReport,"height") * plotResolution
    
    fileName <- paste(gsub("[^a-zA-Z0-9_]","_",caption), as.character(getuuid()),".png",sep="_")
    png(filename=file.path(currentReport@location(),"plots", fileName),
        width=plotPixelWidth,
        height=plotPixelHeight,
        res=plotResolution,
        bg="white")
    eval(call)
    theFile <- getValue(currentReport,"name")
    eText <- paste( escapeHtml(caption) ,"<br/>\n")
    path <- file.path("plots",fileName)
    saveItem(report,substitute(HTMLInsertGraph(file = theFile,GraphFileName=path, Caption=eText, WidthHTML=plotPixelWidth)))
    dev.off()
})

#see below
setGeneric(name="startPlot", def=function(report,caption,dimensions) standardGeneric("startPlot") )
# see below
setMethod("startPlot",signature(report="HtmlReport",caption="character",dimensions="missing"), function(report,caption,dimensions)
        {
  currentReport <- getValue(getValue(report,"masterReport"),"currentReport")         
    plotResolution <- getValue(currentReport,"resolution")
    plotWidth <- getValue(currentReport,"width")
    plotHeight <- getValue(currentReport,"height")
    startPlot(currentReport,caption,dimensions=c(plotWidth,plotHeight,plotResolution))
}) 
#' Starts a multiple statement plot
#' 
#' Plots consisting of multiple statements need to be initialized using this method. 
#' @param report An object of type "Report" that identifies a particular report (only HtmlReport is supported by now).
#' @param caption A character vector containing the caption of the plot
#' @param dimensions a numeric vector with 3 elements: width [inch], height[inch], and resolution[dpi] this argument is optional if missing it will be replaced by the default settings
#' @author Bastian R. Angermann
#' @aliases startPlot startPlot-methods startPlot,HtmlReport,character,numeric-method startPlot,HtmlReport,character,missing-method
#' @export
setMethod("startPlot",signature(report="HtmlReport",caption="character",dimensions="numeric"), function(report,caption,dimensions)
{
    
    currentReport <- getValue(getValue(report,"masterReport"),"currentReport")
    if ( getValue(currentReport, "plotInProgress") )
    {
        # close the old plot first.
        print("Plot in progress. Closing previous plot before opening the new one.")
        finalizePlot(currentReport)
    }

    # try reading the resolution settings
    if ( length(dimensions)>=3 )
        plotResolution <- dimensions[3]
    else
        plotResolution <- getValue(currentReport,"resolution")
    
    plotPixelWidth <- plotResolution*dimensions[1] # guaranteed by method
    # try reading height
    if ( length(dimensions)>=2 )
        plotPixelHeight <- plotResolution*dimensions[2]
    else
        plotPixelHeight <- getValue(currentReport,"height") * plotResolution
    
    fileName <- paste(gsub("[^a-zA-Z0-9_]","_",caption), as.character(getuuid()),".png",sep="_")
    png(filename=file.path(currentReport@location(), "plots", fileName),
        width=plotPixelWidth,
        height=plotPixelHeight,
        res=plotResolution,
        bg="white")
    setValue(currentReport,"plotFile",fileName)
    setValue(currentReport,"plotCaption",caption)
    setValue(currentReport,"plotInProgress",TRUE)
    setValue(currentReport,"plotWidth",plotPixelWidth)
})

#see below
setGeneric(name="finalizePlot", def=function(report) standardGeneric("finalizePlot") )
#' Ends a multiple statement plot
#' 
#' Plots consisting of multiple statements need to be finalized using this method. 
#' @param report An object of type "Report" that identifies a particular report (only HtmlReport is supported by now).
#' @author Bastian R. Angermann
#' @aliases finalizePlot finalizePlot-methods finalizePlot,HtmlReport-method
#' @export
setMethod("finalizePlot",signature(report="HtmlReport"), function(report)
{
    print(report)
    
   # browser()
    currentReport <- getValue(getValue(report,"masterReport"),"currentReport")
    if ( getValue(currentReport, "plotInProgress") )
    {  
        fileName <- getValue(currentReport,"plotFile") 
        caption <- getValue(currentReport,"plotCaption")
        plotPixelWidth <- getValue(currentReport,"plotWidth")
        theFile <-getValue(currentReport,"name")
        eText <- escapeHtml(caption)
        path <- file.path("plots",fileName)
        saveItem(report,substitute(HTMLInsertGraph(file = theFile,GraphFileName=path, Caption=eText, WidthHTML=plotPixelWidth )))
        dev.off()
    } else
    {
        print("No plot in progress. Nothing to finalize.")
    }
    setValue(currentReport,"plotInProgress",FALSE)
})


#see below
setGeneric(name="setLocation", def=function(report, location) standardGeneric("setLocation") )

#' Set the target directory of a report
#' 
#' The directory into which the report is written is set. 
#' @param report An object of type "Report" that identifies a particular report.
#' @param location A character string specifying the path of the directory to which the report should be written.
#' @author Bastian R. Angermann
#' @export 
#' @aliases setLocation setLocation-methods setLocation,Report,character-method
setMethod("setLocation",signature(report="Report",location ="character"), function(report, location)
{
    # do not attempt to find the current report, only set the location for the report it's called for. no surprises.
    if ( getValue(report,"reportInProgress") )
        warning("A report is in progress setLocation will be ignored.")
    else
        setValue(report, "location", location)
})
